use grep::regex::RegexMatcher;
use grep::searcher::{sinks, BinaryDetection, SearcherBuilder};
use rig::{completion::ToolDefinition, tool::Tool};
use serde::{Deserialize, Serialize};
use serde_json::json;
use walkdir::WalkDir;

#[derive(Deserialize)]
pub struct SearchFileArgs {
    content: String,
}

#[derive(Debug, thiserror::Error)]
#[error("Math error")]
pub struct SearchFileError;

#[derive(Deserialize, Serialize)]
pub struct SearchFileCmd {
    pub project_path: String,
}

impl Tool for SearchFileCmd {
    const NAME: &'static str = "file_search";

    type Error = SearchFileError;
    type Args = SearchFileArgs;
    type Output = String;

    async fn definition(&self, _prompt: String) -> ToolDefinition {
        ToolDefinition {
            name: "file_search".to_string(),
            description: "正则搜索工具，根据正则搜索文件内容".to_string(),
            parameters: json!({
                "type": "object",
                "properties": {
                    "content": {
                        "type": "string",
                        "description": "需要搜索的正则表达式"
                    }
                }
            }),
        }
    }

    async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
        let project_path = &self.project_path;
        let content = args.content;

        // 查询结果
        let matcher =
            RegexMatcher::new_line_matcher(content.as_str()).map_err(|e| SearchFileError)?;
        let mut searcher = SearcherBuilder::new()
            .binary_detection(BinaryDetection::quit(b'\x00'))
            .line_number(true)
            .before_context(10)
            .after_context(10)
            .build();
        let mut grep_result: Vec<String> = vec![];
        let mut sink = sinks::Lossy(|line_num, line| {
            grep_result.push(format!("{}: {}", line_num, line));
            Ok(true)
        });
        for result in WalkDir::new(project_path) {
            let dent = match result {
                Ok(dent) => dent,
                Err(err) => {
                    eprintln!("{}", err);
                    continue;
                }
            };
            if !dent.file_type().is_file() {
                continue;
            }
            let result = searcher.search_path(&matcher, dent.path(), &mut sink);
            if let Err(err) = result {
                println!("{}: {}", dent.path().display(), err);
            }
        }

        Ok(grep_result.join("\n").to_string())
    }
}

#[test]
fn testRipgrep() -> Result<(), Box<dyn std::error::Error>> {
    let matcher = RegexMatcher::new_line_matcher(&"fn testRipgre.")?;
    let mut searcher = SearcherBuilder::new()
        .binary_detection(BinaryDetection::quit(b'\x00'))
        .line_number(true)
        .before_context(3)
        .after_context(3)
        .build();
    let mut grep_result: Vec<String> = vec![];
    let mut sink = sinks::Lossy(|line_num, line| {
        grep_result.push(format!("{}: {}", line_num, line));
        Ok(true)
    });
    for result in WalkDir::new("/home/x/codes/rust/aicode/") {
        let dent = match result {
            Ok(dent) => dent,
            Err(err) => {
                eprintln!("{}", err);
                continue;
            }
        };
        if !dent.file_type().is_file() {
            continue;
        }
        let result = searcher.search_path(&matcher, dent.path(), &mut sink);
        if let Err(err) = result {
            println!("{}: {}", dent.path().display(), err);
        }
    }

    // 输出
    println!("{}", grep_result.join("\n"));

    Ok(())
}
